package pay

import (
	"context"
	"errors"
	"feng-che.com/server/driver-quiz/internal/constants"
	"feng-che.com/server/driver-quiz/internal/dep_db"
	"feng-che.com/server/driver-quiz/internal/sysLogger"
	"github.com/sirupsen/logrus"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"mongoDB"
	"time"
)

type Payment struct {
	mongoDB.MongoDefaultModelNonID `bson:"basic"`
	ID                             primitive.ObjectID `bson:"_id,omitempty" json:"id"`
	Money                          int                `gorm:"comment:支付金额，分;check:money > 0" json:"money"`
	Description                    string             `gorm:"comment:支付的相关描述" json:"description"`
	Channel                        PayChannel         `gorm:"comment:支付渠道" json:"channel"`
	PreparePayId                   string             `bson:"prepare_pay_id" json:"preparePayId"`
	State                          PaymentStateText   `bson:"state" json:"state"`
	PayDoneTime                    time.Time          `bson:"pay_done_time" json:"payDoneTime"` //支付完成时间
	Expire                         time.Time          `json:"expire"`                           //过期时间
	PayerInfo                      PayerInfo          `gorm:"comment:发起支付人openId,或者其他代号" bson:"payer_info" json:"-"`
	Admin                          AdminInfo          `bson:"admin" json:"admin"` //审核创建管理员
	Invoice                        Invoice            `bson:"invoice" json:"-"`
	Cart                           []ShoppingCart     `bson:"shopping_cart" json:"shoppingCart"` //购物车
}

// PushNewMerchandise 添加新的购物内容 - 做同类合并
func (p *Payment) PushNewMerchandise(merchandiseID *primitive.ObjectID, amount int) {
	for i := 0; i < len(p.Cart); i++ { // 做同类合并
		if p.Cart[i].ServiceID == *merchandiseID {
			p.Cart[i].Total += amount
			return
		}
	}
	merchandise := ShoppingCart{
		ServiceID: *merchandiseID,
		Total:     amount,
	}
	p.Cart = append(p.Cart, merchandise)
}

type ShoppingCart struct {
	ServiceID primitive.ObjectID `bson:"service" json:"service"`
	Total     int
}

type PaymentStateText string

type Invoice struct {
	Name   string `json:"name" bson:"name"`     //收票人名称
	Tel    string `json:"tel" bson:"tel"`       //收票人电话
	Number string `json:"number" bson:"number"` //发票号码，开票后可以回填
}

type PayChannel string

const (
	ChannelWX PayChannel = "微信支付"
)

const (
	PayDone    PaymentStateText = "完成"
	RequestPay PaymentStateText = "发起支付"
)

func (p *Payment) Update(ctx context.Context, dataFields *bson.D, options ...*options.UpdateOptions) error {
	*dataFields = append(*dataFields, bson.E{Key: "$set", Value: bson.D{
		*mongoDB.CommonUpdateTimeFieldWithCurrentTime(),
	}})
	_, err := p.GetCollection().UpdateByID(ctx, p.ID, dataFields, options...)
	return err
}

func (p *Payment) Delete(ctx context.Context) error {
	//TODO implement me
	panic("implement me")
}

func (p *Payment) GetCollectionName() string {
	return "payment"
}

func (p *Payment) GetCollection() *mongo.Collection {
	name := p.GetCollectionName()
	return dep_db.ObjectCollection(&name)
}

// NewPayment 创建payment
func NewPayment(description string, channel PayChannel, expire time.Time) *Payment {
	paymentItem := &Payment{
		Money:       0,
		Description: description,
		Channel:     channel,
		Expire:      expire,
		State:       RequestPay,
		Cart:        []ShoppingCart{},
	}
	return paymentItem
}

type PaymentItemDetail struct {
	OrderID     primitive.ObjectID `bson:"order_id"`
	Money       int
	OrderStatus constants.StatusOfOrder //支付发起时候订单的状态
}

type AdminInfo struct {
	ID        primitive.ObjectID `bson:"id" json:"id"`
	LoginName string             `bson:"login_name" json:"loginName"`
	Name      string             `json:"name"`
}

type PayerInfo struct {
	ID       primitive.ObjectID `bson:"id" json:"id"`
	Name     string             `json:"name"`
	Nickname string             `json:"nickname" bson:"nickname"`
	OpenID   string             `json:"open_id" bson:"open_id"`
	PayerIp  string             `gorm:"comment:发起支付人的ip" bson:"payer_ip" json:"-"`
}

// Save 更新payment
func (p *Payment) Save(ctx context.Context) (err error) {
	if p.ID.IsZero() {
		p.ID = primitive.NewObjectID()
		err = p.InsertToDB(ctx)
		return
	} else {
		p.UpdatedAt = time.Now()
		_, err = p.GetCollection().UpdateByID(
			ctx,
			p.ID,
			bson.D{
				{"$set", p},
			},
		)
		return
	}
}

// SaveToDB 创建预付卡到数据库
func (p *Payment) InsertToDB(ctx context.Context) error {
	p.Create()
	insertOne, err := p.GetCollection().InsertOne(
		ctx,
		p,
	)
	if err != nil {
		return err
	}
	p.ID = insertOne.InsertedID.(primitive.ObjectID)
	return nil
}

// 根据id查找payment实例,id可以是objectid的字符串
func QueryPaymentById[T string | primitive.ObjectID](id T) (*Payment, error) {
	var _id any
	var paymentID primitive.ObjectID
	var err error
	_id = id
	switch _id.(type) {
	case string:
		paymentID, err = primitive.ObjectIDFromHex(_id.(string))
		if err != nil {
			return nil, err
		}
	default:
		paymentID = _id.(primitive.ObjectID)
	}
	result := new(Payment)
	sr := mongoDB.QueryOne(
		result.GetCollection(),
		nil,
		bson.D{
			{"_id", paymentID},
		},
	)
	if sr.Err() != nil {
		return nil, sr.Err()
	}
	err = sr.Decode(result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// SetItBeenUsed 设置payment为已经处理过支付回调状态，避免重复处理
// 理论上收到的支付金额应该和发起支付的金额相同
func (p *Payment) SetItBeenUsed(ctx context.Context) error {
	p.UpdatedAt = time.Now()
	p.State = PayDone
	_, err := p.GetCollection().UpdateOne(
		ctx,
		bson.D{
			{"_id", p.ID},
		},
		bson.D{
			{"$set", bson.M{
				"state":            p.State,
				"basic.updated_at": p.UpdatedAt,
			}},
		},
	)
	return err
}

// QueryPaymentsByTime 时间范围查询支付
func QueryUnPayPaymentsByTime(ctx context.Context, start *time.Time, end *time.Time) (*[]Payment, error) {
	if end == nil {
		tmp := time.Now()
		end = &tmp
	}
	if start == nil {
		return nil, errors.New("没有指定开始时间")
	}
	payments := make([]Payment, 0)
	err := mongoDB.QueryManyNoCountOnDocument(
		(&Payment{}).GetCollection(),
		ctx,
		&payments,
		bson.D{
			{"basic.created_at", bson.M{
				"$gte": *start,
				"$lt":  *end,
			}},
			{"state", RequestPay},
		},
	)
	if err != nil {
		return nil, err
	}
	return &payments, nil
}

// UpdatePrepayID 在发起支付后，更新payment的prepayID
func (p *Payment) UpdatePrepayID(ctx context.Context, id *string) {
	p.PreparePayId = *id
	err := p.Update(ctx, &bson.D{
		{"$set", bson.D{
			{"prepare_pay_id", p.PreparePayId},
		}},
	})
	if err != nil {
		sysLogger.SysLog.WithFields(logrus.Fields{
			"msg":       err.Error(),
			"paymentID": p.ID.Hex(),
		}).Warn("更新payment的prepareID错误")
	}
}

// UpdatePayDoneState 设置支付完成时间
func (p *Payment) UpdatePayDoneState(ctx context.Context) error {
	now := time.Now()
	err := p.Update(ctx, &bson.D{
		{"$set", bson.D{
			{"state", PayDone},
			{"pay_done_time", now},
		}},
	})
	if err != nil {
		sysLogger.SysLog.WithFields(logrus.Fields{
			"msg":       err.Error(),
			"paymentID": p.ID.Hex(),
		}).Error("更新支付完成状态错误")
	}
	p.State = PayDone
	p.PayDoneTime = now
	return err
}
